#pragma once

template<class T>
class SharedPtr
{
protected:
	T* pRep;
	unsigned int* pUseCount;

public:
	SharedPtr() : pRep(0), pUseCount(0)
    {
    }

    template<class T>
	explicit SharedPtr(T* rep) : pRep(rep), pUseCount(new unsigned int(1))
	{
	}

	SharedPtr(const SharedPtr& r)
        : pRep(0), pUseCount(0)
	{
	    pRep = r.pRep;
	    pUseCount = r.pUseCount; 

	    if (pUseCount)
		    ++(*pUseCount); 
	}
	SharedPtr& operator=(const SharedPtr& r) {
		if (pRep == r.pRep)
			return *this;

		SharedPtr<T> tmp(r);
		swap(tmp);
		return *this;
	}
	
	template< class Y>
	SharedPtr(const SharedPtr<Y>& r)
        : pRep(0), pUseCount(0)
	{
	    pRep = r.getPointer();
	    pUseCount = r.useCountPointer();

	    if(pUseCount)
		    ++(*pUseCount);
	}
	template< class Y>
	SharedPtr& operator=(const SharedPtr<Y>& r) {
		if (pRep == r.pRep)
			return *this;
		// Swap current data into a local copy
		// this ensures we deal with rhs and this being dependent
		SharedPtr<T> tmp(r);
		swap(tmp);
		return *this;
	}
	virtual ~SharedPtr() {
        release();
	}


	inline T& operator*() const { assert(pRep); return *pRep; }
	inline T* operator->() const { assert(pRep); return pRep; }
	inline T* get() const { return pRep; }

	/** Binds rep to the SharedPtr.
		@remarks
			Assumes that the SharedPtr is uninitialised!
	*/
	void bind(T* rep) {
		assert(!pRep && !pUseCount);
		pUseCount = new unsigned int(1);
		pRep = rep;
	}

	inline bool unique() const { assert(pUseCount); return *pUseCount == 1; }
	inline unsigned int useCount() const { assert(pUseCount); return *pUseCount; }
	inline unsigned int* useCountPointer() const { return pUseCount; }

	inline T* getPointer() const { return pRep; }

	inline bool isNull(void) const { return pRep == 0; }

    inline void setNull(void)
	{ 
		if (pRep == NULL)
			return;

		release();
		pRep = 0;
		pUseCount = 0;
    }

protected:

    inline void release(void)
    {
		bool destroyThis = false;

		if (pUseCount)
		{
			if (--(*pUseCount) == 0)
				destroyThis = true;
		}

		if (destroyThis)
			destroy();
    }

    virtual void destroy(void)
    {
        delete pRep;
        delete pUseCount;
    }

	virtual void swap(SharedPtr<T> &other) 
	{
		std::swap(pRep, other.pRep);
		std::swap(pUseCount, other.pUseCount);
	}
};